#ifndef CLIENTSYNCREQUEST_H_
#define CLIENTSYNCREQUEST_H_

#include "scatu/ClientRequests.h"

namespace scatu {

// Declaration for assignable and non-assignable types
template<TServerFunction Function, typename ReturnType, typename ArgsType, bool ReturnIsAssignable>
class RSynchronousClientRequestBase {};

// Assignable return type.
template<TServerFunction Function, typename ReturnType, typename ArgsType>
class RSynchronousClientRequestBase<Function, ReturnType, ArgsType, true>
  : public RClientRequestCommon
    {
public:
    
    RSynchronousClientRequestBase() : RClientRequestCommon(Function) {}
    
    void CallL(RSessionBase & aSession, ReturnType & aRet, ArgsType const & aArgs)
        {
        // Retry once if server has terminated
        TRAPD(err, DoCallL(aSession, aRet, aArgs));
        if(err == KErrServerTerminated)
            {
            User::LeaveIfError(aSession.Connect());
            DoCallL(aSession, aRet, aArgs);
            }
        else
            { User::LeaveIfError(err); }
        }
    
private:
    
    void DoCallL(RSessionBase & aSession, ReturnType & aRet, ArgsType const & aArgs)
        {
        HBufC8 * argsBuf = aArgs.SerializeL();
        CleanupStack::PushL(argsBuf);
        TInt size = 0;
        TPckg<TInt> sizeBuf(size);
        User::LeaveIfError(aSession.SendReceive(Function, TIpcArgs(argsBuf, &sizeBuf)));
        CleanupStack::PopAndDestroy(argsBuf);

        HBufC8 * returnBuf = ReadReturnValueLC(aSession, size);
        aRet.DeserializeL(*returnBuf);
        CleanupStack::PopAndDestroy(returnBuf);
        }
    
    };

// Not assignable return type (return void) 
template<TServerFunction Function, typename ReturnType, typename ArgsType>
class RSynchronousClientRequestBase<Function, ReturnType, ArgsType, false>
  : public RClientRequestCommon
    {
public:
    RSynchronousClientRequestBase() : RClientRequestCommon(Function) {}
    
    void CallL(RSessionBase & aSession, ArgsType const & aArgs)
        {
        // Retry once if server has terminated
        TRAPD(err, DoCallL(aSession, aArgs));
        if(err == KErrServerTerminated)
            {
            User::LeaveIfError(aSession.Connect());
            DoCallL(aSession, aArgs);
            }
        else
            { User::LeaveIfError(err); }
        }
    
private:
    
    void DoCallL(RSessionBase & aSession, ArgsType const & aArgs)
        {
        HBufC8 * argsBuf = aArgs.SerializeL();
        CleanupStack::PushL(argsBuf);
        User::LeaveIfError(aSession.SendReceive(Function, TIpcArgs(argsBuf)));
        CleanupStack::PopAndDestroy(argsBuf);
        }
    };

// Automatic selection of right template, This is what should be used!
template<TServerFunction Function, typename ReturnType, typename ArgsType>
class RSynchronousRequest<EClient, Function, ReturnType, ArgsType>
  : public RSynchronousClientRequestBase<Function, ReturnType, ArgsType, ReturnType::IsAssignable>
{
    typedef ReturnType TReturnType;
};

} // namespace scatu

#endif /* CLIENTSYNCREQUEST_H_ */
